﻿using System;
using Ewk.Math.ComputationTypes;

namespace Ewk.Math.Algebra
{
    /// <summary>
    /// Contains methods that extend a <see cref="Matrix{T}"/>
    /// </summary>
    public static class MatrixSearch
    {
        /// <summary>
        /// Value that represents an item was not found.
        /// </summary>
        public const int NotFound = -1;

        /// <summary>
        /// Gets the first row that contains an element in the provided column number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetFirstRowNumberContainingZero<T>(this Matrix<T> matrix,
                                                             int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetFirstRowNumberContainingZero(0, columnNumber);
        }

        /// <summary>
        /// Gets the first row that contains an element in the provided column number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumberToStartFrom">The row number to start the searh from.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetFirstRowNumberContainingZero<T>(this Matrix<T> matrix,
                                                             int rowNumberToStartFrom,
                                                             int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            for (var i = rowNumberToStartFrom; i < matrix.RowCount; i++)
            {
                if (matrix[i, columnNumber].AlmostEquals(Computable<T>.Factory.Zero))
                {
                    return i;
                }
            }

            return NotFound;
        }

        /// <summary>
        /// Gets the first row that contains an element in the provided column number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetFirstRowNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                                int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetFirstRowNumberContainingZero(0, columnNumber);
        }

        /// <summary>
        /// Gets the first row that contains an element in the provided column number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumberToStartFrom">The row number to start the searh from.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetFirstRowNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                                int rowNumberToStartFrom,
                                                                int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            for (var i = rowNumberToStartFrom; i < matrix.RowCount; i++)
            {
                if (!matrix[i, columnNumber].AlmostEquals(Computable<T>.Factory.Zero))
                {
                    return i;
                }
            }

            return NotFound;
        }

        /// <summary>
        /// Gets the last row that contains an element in the provided column number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetLastRowNumberContainingZero<T>(this Matrix<T> matrix,
                                                            int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetLastRowNumberContainingNonZero(0, columnNumber);
        }

        /// <summary>
        /// Gets the last row that contains an element in the provided column number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumberToStartFrom">The row number to start the searh from.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetLastRowNumberContainingZero<T>(this Matrix<T> matrix,
                                                            int rowNumberToStartFrom,
                                                            int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            var lastRowNumber = NotFound;

            for (var i = rowNumberToStartFrom; i < matrix.RowCount; i++)
            {
                if (!matrix[i, columnNumber].AlmostEquals(Computable<T>.Factory.Zero)) continue;

                lastRowNumber = i;
            }

            return lastRowNumber;
        }

        /// <summary>
        /// Gets the last row that contains an element in the provided column number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetLastRowNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                               int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetLastRowNumberContainingNonZero(0, columnNumber);
        }

        /// <summary>
        /// Gets the last row that contains an element in the provided column number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumberToStartFrom">The row number to start the searh from.</param>
        /// <param name="columnNumber">The column number to look in.</param>
        public static int GetLastRowNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                               int rowNumberToStartFrom,
                                                               int columnNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            var lastRowNumber = NotFound;

            for (var i = rowNumberToStartFrom; i < matrix.RowCount; i++)
            {
                if (matrix[i, columnNumber].AlmostEquals(Computable<T>.Factory.Zero)) continue;

                lastRowNumber = i;
            }

            return lastRowNumber;
        }

        /// <summary>
        /// Gets the first column that contains an element in the provided row number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetFirstColumnNumberContainingZero<T>(this Matrix<T> matrix,
                                                             int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetFirstColumnNumberContainingZero(0, rowNumber);
        }

        /// <summary>
        /// Gets the first column that contains an element in the provided row number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumberToStartFrom">The column number to start the searh from.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetFirstColumnNumberContainingZero<T>(this Matrix<T> matrix,
                                                             int columnNumberToStartFrom,
                                                             int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            for (var i = columnNumberToStartFrom; i < matrix.ColumnCount; i++)
            {
                if (matrix[rowNumber, i].AlmostEquals(Computable<T>.Factory.Zero))
                {
                    return i;
                }
            }

            return NotFound;
        }

        /// <summary>
        /// Gets the first column that contains an element in the provided row number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetFirstColumnNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                                int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetFirstColumnNumberContainingZero(0, rowNumber);
        }

        /// <summary>
        /// Gets the first column that contains an element in the provided row number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumberToStartFrom">The column number to start the searh from.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetFirstColumnNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                                int columnNumberToStartFrom,
                                                                int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            for (var i = columnNumberToStartFrom; i < matrix.ColumnCount; i++)
            {
                if (!matrix[rowNumber, i].AlmostEquals(Computable<T>.Factory.Zero))
                {
                    return i;
                }
            }

            return NotFound;
        }

        /// <summary>
        /// Gets the last column that contains an element in the provided row number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetLastColumnNumberContainingZero<T>(this Matrix<T> matrix,
                                                            int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetLastColumnNumberContainingNonZero(0, rowNumber);
        }

        /// <summary>
        /// Gets the last column that contains an element in the provided row number with a value that represents zero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumberToStartFrom">The column number to start the searh from.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetLastColumnNumberContainingZero<T>(this Matrix<T> matrix,
                                                            int columnNumberToStartFrom,
                                                            int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            var lastColumnNumber = NotFound;

            for (var i = columnNumberToStartFrom; i < matrix.ColumnCount; i++)
            {
                if (!matrix[rowNumber, i].AlmostEquals(Computable<T>.Factory.Zero)) continue;

                lastColumnNumber = i;
            }

            return lastColumnNumber;
        }

        /// <summary>
        /// Gets the last column that contains an element in the provided row number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetLastColumnNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                               int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            return matrix.GetLastColumnNumberContainingNonZero(0, rowNumber);
        }

        /// <summary>
        /// Gets the last column that contains an element in the provided row number with a value that represents nonzero.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> of the elements.</typeparam>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to search.</param>
        /// <param name="columnNumberToStartFrom">The column number to start the searh from.</param>
        /// <param name="rowNumber">The row number to look in.</param>
        public static int GetLastColumnNumberContainingNonZero<T>(this Matrix<T> matrix,
                                                               int columnNumberToStartFrom,
                                                               int rowNumber)
            where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
        {
            var lastColumnNumber = NotFound;

            for (var i = columnNumberToStartFrom; i < matrix.ColumnCount; i++)
            {
                if (matrix[rowNumber, i].AlmostEquals(Computable<T>.Factory.Zero)) continue;

                lastColumnNumber = i;
            }

            return lastColumnNumber;
        }
    }
}